package org.yagnus.datastructure;

/**
 * This represents a range on a totally ordered set BASETYPE
 * 
 * @author Hsb
 * 
 * @param <BASETYPE>
 */
public class RangeImpl<BASETYPE extends Comparable<BASETYPE>> implements
		Range<RangeImpl<BASETYPE>, BASETYPE> {
	final protected boolean openOnTheLeft, openOnTheRight;
	final protected BASETYPE min, max;

	public RangeImpl(BASETYPE min, BASETYPE max, boolean openOnTheLeft,
			boolean openOnTheRight) {
		this.openOnTheLeft = openOnTheLeft;
		this.openOnTheRight = openOnTheRight;
		if (min.compareTo(max) <= 0) {
			this.min = min;
			this.max = max;
		} else {
			this.max = min;
			this.min = max;
		}
	}

	/**
	 * This constructor generates a closed range
	 * 
	 * @param min
	 * @param max
	 */
	public RangeImpl(BASETYPE min, BASETYPE max) {
		this(min, max, false, false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.yagnus.datastructure.Range#isOpenOnTheLeft()
	 */
	@Override
	public boolean isOpenOnTheLeft() {
		return openOnTheLeft;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.yagnus.datastructure.Range#isOpenOnTheRight()
	 */
	@Override
	public boolean isOpenOnTheRight() {
		return openOnTheRight;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.yagnus.datastructure.Range#getMin()
	 */
	@Override
	public BASETYPE getMin() {
		return min;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.yagnus.datastructure.Range#getMax()
	 */
	@Override
	public BASETYPE getMax() {
		return max;
	}

	public <O extends RangeImpl<BASETYPE>> boolean intersects(O o) {
		return intersects(this, o);
	}

	public static <T extends Comparable<T>, R extends Range<R, T>> boolean intersects(
			R r1, R r2) {
		// first take care of the barely touch cases:
		if (r2 == null || r1 == null)
			return false;
		// I'm touching other to my right its left
		if (r2.getMin().equals(r1.getMax()) && !r2.isOpenOnTheLeft()
				&& !r1.isOpenOnTheRight())
			return true;

		// I'm touching other on my left its right
		if (r2.getMax().equals(r1.getMin()) && !r2.isOpenOnTheRight()
				&& !r1.isOpenOnTheLeft())
			return true;

		if (r1.getMin().compareTo(r2.getMin()) < 0) {
			// (my left, his left)
			return r1.getMax().compareTo(r2.getMin()) > 0;
			/** intersects only when (my left, his left, my right) **/
		} else {
			// (his left, my left)
			return r1.getMin().compareTo(r2.getMax()) < 0;
			/** intersects only when (his left, my left, his right) **/
		}
	}

	public static <T extends Comparable<T>, R extends Range<R, T>> RangeImpl<T> intersection(
			R r1, R r2) {

		R smaller = r1;
		R bigger = r2;

		if (intersects(smaller, bigger)) {
			if (r1.getMin().compareTo(r2.getMin()) >= 0) {
				smaller = r2;
				bigger = r1;
			}
			T min = bigger.getMin();
			T max = smaller.getMax();
			if (bigger.getMax().compareTo(max) > 0)
				max = bigger.getMax();
			return new RangeImpl<T>(min, max, smaller.isOpenOnTheLeft(),
					bigger.isOpenOnTheRight());
		}

		return null;

	}

	public boolean equals(Object o) {
		if (o == null || !this.getClass().isAssignableFrom(o.getClass())) {
			return false;
		}

		RangeImpl<BASETYPE> O = (RangeImpl<BASETYPE>) o;

		return (this.min == O.min && this.max == O.max
				&& this.openOnTheLeft == O.openOnTheLeft && this.openOnTheRight == O.openOnTheRight);
	}
}
